package com.example.mytestdemo.scale;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;

import androidx.annotation.Nullable;
import androidx.core.view.GestureDetectorCompat;


/**
 * @Description: 提供一个统一处理RecyclerView 手势操作控件
 * @Author: yujinghua
 * @CreateDate: 2021/3/9 16:43
 */
public class ScaleView extends FrameLayout {
    private static final int DEFAULT_SCALE_DURATION = 300;
    private static final float DEFAULT_SCALE_FACTOR = 1.f;
    private static final float DEFAULT_MAX_SCALE_FACTOR = 2.0f;
    private static final float DEFAULT_MIN_SCALE_FACTOR = 1.0f;
    private static final String PROPERTY_SCALE = "scale";
    private static final String PROPERTY_TRANX = "tranX";
    private static final String PROPERTY_TRANY = "tranY";
    private static final float INVALID_TOUCH_POSITION = -1;


    float mMaxScaleFactor;      // 最大缩放系数
    float mMinScaleFactor;      // 最小缩放系数
    float mDefaultScaleFactor;  // 默认缩放系数 双击缩小后的缩放系数 暂不支持小于1
    int mScaleDuration;         // 缩放时间 ms

    float originWidth;      //原始的宽度
    float originHeight;     //原始的高度
    float mViewWidth;       // 宽度
    float mViewHeight;      // 高度
    float mScaleFactor;     // 缩放系数

    boolean isScaling = false;    // 是否正在缩放

    ScaleGestureDetector mScaleDetector;
    GestureDetectorCompat mGestureDetector;

    float mMaxTranX;        // 当前缩放系数下最大的X偏移量
    float mMaxTranY;        // 当前缩放系数下最大的Y偏移量


    float mTranX;           // x偏移量
    float mTranY;           // y偏移量

    private boolean candoOperation = false;  //是否禁止操作

    public void setProhibitOperation(boolean candoOperation){
        this.candoOperation = candoOperation;
    }

    public interface RecyclerDrawListener{
        void setPoint(MotionEvent e, float scale, float tranX, float tranY);
    }
    private RecyclerDrawListener recyclerDrawListener;
    public void setRecyclerDrawListener(RecyclerDrawListener recyclerDrawListener){
        this.recyclerDrawListener = recyclerDrawListener;
    }


    public ScaleView(Context context) {
        this(context, null);
    }

    public ScaleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScaleView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        mMaxScaleFactor = DEFAULT_MAX_SCALE_FACTOR;
        mMinScaleFactor = DEFAULT_MIN_SCALE_FACTOR;
        mDefaultScaleFactor = DEFAULT_SCALE_FACTOR;
        mScaleFactor = mDefaultScaleFactor;
        mScaleDuration = DEFAULT_SCALE_DURATION;

        mScaleDetector = new ScaleGestureDetector(getContext(), new CardScaleListener());
        mGestureDetector = new GestureDetectorCompat(getContext(), new CardGestureListener());
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        originWidth = MeasureSpec.getSize(widthSpec);
        originHeight = MeasureSpec.getSize(heightSpec);
        mViewWidth = originWidth;
        mViewHeight = originHeight;
        super.onMeasure(widthSpec, heightSpec);
    }


    float mLastTouchX;      // 上一次触摸位置 X
    float mLastTouchY;      // 上一次触摸位置 Y

    float tempDownX;
    float tempDownY;
    float deviationX;  //x移到偏移量
    float deviationY;  //y移到偏移量

    /**
     * 手势事件回调
     * @param e
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent e) {
//        if(!candoOperation) return true;
        if(!canOperation) return true;

        if(isDoing) return true;
        if(e.getToolType(0) != MotionEvent.TOOL_TYPE_FINGER){
            if(recyclerDrawListener != null){
                recyclerDrawListener.setPoint(e, mScaleFactor, mTranX, mTranY);
            }
            return true;
        }
        if (e.getToolType(0) == MotionEvent.TOOL_TYPE_FINGER) {

//            if(e.getPointerCount() > 1){
                mScaleDetector.onTouchEvent(e);

//            }
            mGestureDetector.onTouchEvent(e);

            float tempx = getX();
            float tempy = getY();
            if (((tempx != 0 || tempy !=0) || mScaleFactor != DEFAULT_SCALE_FACTOR) && !isScaling && e.getPointerCount() == 1) {

                int action = e.getAction();
                Log.d("TAG tgw", "onTouchEvent: action:"+action);
                Log.d("TAG tgw", "onTouchEvent: tempx:"+tempx);
                Log.d("TAG tgw", "onTouchEvent: tempy:"+tempy);
                switch (action) {
                    case MotionEvent.ACTION_DOWN:

                        float x = e.getRawX();
                        float y = e.getRawY();
                        mLastTouchX = x;
                        mLastTouchY = y;

                        break;
                    case MotionEvent.ACTION_MOVE:

                        if(!isScaling){
                            try {

                                x = e.getRawX();
                                y = e.getRawY();

                                float dx = x - mLastTouchX;
                                float dy = y - mLastTouchY;

                                setTranslateXY(mTranX + dx, mTranY + dy);
                                localSet(false);
                                setXY(mTranX, mTranY);

                                mLastTouchX = x;
                                mLastTouchY = y;

                            } catch (Exception e1) {

                            }
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        if(cardViewChangeListener != null){
                            cardViewChangeListener.viewChange();
                        }
                        break;
                }

                return super.onTouchEvent(e);
                // return true;

            }
        }
        return super.onTouchEvent(e);
    }

    /**
     * 手势操作边界值处理
     * @param needAnimation
     */
    private void localSet(boolean needAnimation){

        float newTempX = mTranX;
        float newTempY = mTranY;

        if(mTranX > 0){
            //mTranX = 0;
            newTempX = 0;
        }else if((mTranX + originWidth*mScaleFactor) < originWidth){
            //mTranX = originWidth - originWidth*mScaleFactor;
            newTempX = originWidth - originWidth*mScaleFactor;
        }

        if(mTranY > 0){
            //mTranY = 0;
            newTempY = 0;
        }else if((mTranY + originHeight*mScaleFactor) < originHeight){
            //mTranY = originHeight - originHeight*mScaleFactor;
            newTempY = originHeight - originHeight*mScaleFactor;
        }

        if(!(newTempX == mTranX && newTempY == mTranY) && needAnimation){
            justMove(mTranX, mTranY, newTempX, newTempY);
        }else if(needAnimation){

            mTranX = newTempX;
            mTranY = newTempY;
            //这个现在的功能就是给同步发送用的
            if(cardViewChangeListener != null){
                cardViewChangeListener.viewChange();
            }
        } else{
            mTranX = newTempX;
            mTranY = newTempY;
        }

    }

    private void setXY(float XX, float YY){
        float tempX = (mScaleFactor - 1) * originWidth / 2 + XX;
        float tempY = (mScaleFactor - 1) * originHeight / 2 + YY;
        setX(tempX);
        setY(tempY);

    }


    private void setTranslateXY(float tranX, float tranY) {
        mTranX = tranX;
        mTranY = tranY;

    }

    /**
     * 针对缩放手势包装
     */
    private class CardScaleListener implements ScaleGestureDetector.OnScaleGestureListener {
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            isScaling = true;
            deviationX += (mTranX - tempDownX);
            deviationY += (mTranY - tempDownY);

            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {

            float tempScaleFactor = mScaleFactor;
            float tempLastScale = detector.getScaleFactor();
            tempScaleFactor *= tempLastScale;

            tempScaleFactor = Math.max(mMinScaleFactor, Math.min(tempScaleFactor, mMaxScaleFactor));

            mScaleFactor = tempScaleFactor;


            float xx = 0 - ((tempScaleFactor - 1) * mViewWidth) / 2  + deviationX;
            float yy = 0 - ((tempScaleFactor - 1) * mViewHeight) / 2 + deviationY;

            setTranslateXY(xx, yy);

            isScaling = true;

            setScaleX(mScaleFactor);
            setScaleY(mScaleFactor);

            return false;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {

            tempDownX = mTranX;
            tempDownY = mTranY;


            isScaling = false;
            canOperation = false;

            new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                @Override
                public void run() {
                    canOperation = true;
                }
            }, 100);

            localSet(true);
        }
    }
    private boolean canOperation = true;


    /**
     * 双击手势操作
     */
    private class CardGestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            if(isDoing) return super.onDoubleTap(e);


            float startFactor = mScaleFactor;
            float endFactor;
            boolean toBig = true;
            float mScaleCenterX;    // 缩放中心 X
            float mScaleCenterY;    // 缩放中心 Y
            if (mScaleFactor == mDefaultScaleFactor) {
                mScaleCenterX = e.getX();
                mScaleCenterY = e.getY();
                endFactor = mMaxScaleFactor;
                mScaleFactor = mMaxScaleFactor;
                toBig = true;
            } else {
                mScaleCenterX = mScaleFactor == 1 ? e.getX() : -mTranX / (mScaleFactor - 1);
                mScaleCenterY = mScaleFactor == 1 ? e.getY() : -mTranY / (mScaleFactor - 1);
                endFactor = mDefaultScaleFactor;
                mScaleFactor = mDefaultScaleFactor;
                toBig = false;
            }


            zoom(startFactor, endFactor, toBig, mScaleCenterX, mScaleCenterY);
            return super.onDoubleTap(e);
        }

    }


    ValueAnimator mScaleAnimator;

    boolean isDoing = false;

    /**
     * 目标手势操作
     * @param startVal
     * @param endVal
     * @param toBig
     * @param mScaleCenterX
     * @param mScaleCenterY
     */
    private void zoom(float startVal, float endVal, boolean toBig, float mScaleCenterX, float mScaleCenterY) {
        if (mScaleAnimator == null) {
            newZoomAnimation();
        }

        if (mScaleAnimator.isRunning()) {
            return;
        }
        isDoing = true;
        //set Value
        mMaxTranX = mViewWidth - (mViewWidth * endVal);
        mMaxTranY = mViewHeight - (mViewHeight * endVal);

        float startTranX = mTranX;
        float startTranY = mTranY;

        float endTranX;
        float endTranY;
        if(toBig){
            endTranX = mTranX - (endVal - startVal) * mScaleCenterX;
            endTranY = mTranY - (endVal - startVal) * mScaleCenterY;
        }else{
            if(endVal == mDefaultScaleFactor){
                endTranX = 0;
                endTranY = 0;
                deviationX = 0;
                deviationY = 0;
                tempDownX = 0;
                tempDownY = 0;
            }else{
                endTranX = mTranX - (endVal - startVal) * mScaleCenterX;
                endTranY = mTranY - (endVal - startVal) * mScaleCenterY;
            }

        }

        PropertyValuesHolder scaleHolder = PropertyValuesHolder
                .ofFloat(PROPERTY_SCALE, startVal, endVal);
        PropertyValuesHolder tranXHolder = PropertyValuesHolder
                .ofFloat(PROPERTY_TRANX, startTranX, endTranX);
        PropertyValuesHolder tranYHolder = PropertyValuesHolder
                .ofFloat(PROPERTY_TRANY, startTranY, endTranY);
        mScaleAnimator.setValues(scaleHolder, tranXHolder, tranYHolder);
        mScaleAnimator.setDuration(mScaleDuration);
        mScaleAnimator.start();
    }

    private void newZoomAnimation() {
        mScaleAnimator = new ValueAnimator();
        mScaleAnimator.setInterpolator(new DecelerateInterpolator());
        mScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //update scaleFactor & tranX & tranY
                mScaleFactor = (float) animation.getAnimatedValue(PROPERTY_SCALE);
                setTranslateXY(
                        (float) animation.getAnimatedValue(PROPERTY_TRANX),
                        (float) animation.getAnimatedValue(PROPERTY_TRANY)
                );


//                setX(mTranX);
//                setY(mTranY);
                setScaleX(mScaleFactor);
                setScaleY(mScaleFactor);

                setXY(mTranX, mTranY);
            }
        });


        mScaleAnimator.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationStart(Animator animation) {
                isScaling = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                isScaling = false;
                isDoing = false;

                //Log.e("22222", "mTranX="+mTranX+" ||mTranY="+mTranY + " ||viewX="+getX()+" ||viewY="+getY());
                if(cardViewChangeListener != null){
                    cardViewChangeListener.viewChange();
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                isScaling = false;
            }

        });

    }



    ValueAnimator mMoveAnimator;
    private void justMove(float startTranX, float startTranY, float endTranX, float endTranY){

        if(mMoveAnimator == null){
            newJustMove();
        }
        if(mMoveAnimator.isRunning()){
            return;
        }

        PropertyValuesHolder tranXHolder = PropertyValuesHolder
                .ofFloat(PROPERTY_TRANX, startTranX, endTranX);
        PropertyValuesHolder tranYHolder = PropertyValuesHolder
                .ofFloat(PROPERTY_TRANY, startTranY, endTranY);

        mMoveAnimator.setValues(tranXHolder, tranYHolder);
        mMoveAnimator.setDuration(mScaleDuration);
        mMoveAnimator.start();
    }

    /**
     * 手势动画
     */
    private void newJustMove(){
        mMoveAnimator = new ValueAnimator();
        mMoveAnimator.setInterpolator(new DecelerateInterpolator());
        mMoveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setTranslateXY(
                        (float) animation.getAnimatedValue(PROPERTY_TRANX),
                        (float) animation.getAnimatedValue(PROPERTY_TRANY)
                );
                setXY(mTranX, mTranY);
            }
        });

        mMoveAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                isScaling = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {

                isScaling = false;

                if(cardViewChangeListener != null){
                    cardViewChangeListener.viewChange();
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                isScaling = false;
            }
        });
    }


    /**
     * 监听RecyclerView变化，放大缩小，移动
     */
    public interface CardViewChangeListener{
        void viewChange();
    }

    private CardViewChangeListener cardViewChangeListener;

    public void setCardViewChangeListener(CardViewChangeListener listener){
        this.cardViewChangeListener = listener;
    }

    /**
     * 获取手势同步信息
     * @param cardSyncMessage
     */
//    public void sendSyncMessage(SyncMessage cardSyncMessage){
//        cardSyncMessage.offsetX = getX();
//        cardSyncMessage.offsetY = getY();
//        cardSyncMessage.scale = mScaleFactor;
//    }

    /**
     * 同步手势操作
     * @param xx  x坐标平移量
     * @param yy  y坐标平移量
     * @param scale  缩放
     */
    public void doCardViewChange(float xx, float yy, float scale){

        if(xx == getX() && yy == getY() && scale == mScaleFactor) return;

        if(scale != mScaleFactor){
            //有缩放
            calculationScale(scale, xx, yy);
        }else{
            float currentScale = mScaleFactor;
            float TurnTargetX = xx - (currentScale - 1) * originWidth / 2;
            float TurnTargetY = yy - (currentScale - 1) * originHeight / 2;
            setTranslateXY(TurnTargetX, TurnTargetY);
            localSet(false);
            setXY(mTranX, mTranY);
        }
    }
    private void calculationScale(float targetScale, float targetX, float targetY){
        float currentScale = mScaleFactor;
        boolean toBig = true;
        float currentX = mTranX;
        float currentY = mTranY;
        //Log.e("65656565", "targetX="+targetX + "||targetY="+targetY + "||getx="+getX() + "||gety="+getY());
        float TurnTargetX = targetX - (targetScale - 1) * originWidth / 2;
        float TurnTargetY = targetY - (targetScale - 1) * originHeight / 2;
        float centerX = (currentX - TurnTargetX)/(targetScale - currentScale);
        float centerY = (currentY - TurnTargetY)/(targetScale - currentScale);
        if(targetScale > currentScale){
            toBig = true;
            zoom(currentScale, targetScale, toBig, centerX, centerY);
        }else if(targetScale < currentScale){
            toBig = false;
            zoom(currentScale, targetScale, toBig, centerX, centerY);
        }
    }
}
